package com.cellulam.api.config;

import com.cellulam.framework.core.exceptions.ForbidAnonymousException;
import com.cellulam.framework.core.exceptions.UnauthorizedException;
import com.cellulam.framework.core.model.DataResult;
import com.cellulam.framework.core.exceptions.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.spring.MyBatisSystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

@ControllerAdvice
@ResponseBody
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class WebControllerAdvice {

    private static final Logger logger = LoggerFactory.getLogger(WebControllerAdvice.class);

    @ExceptionHandler(Exception.class)
    public DataResult handleException(Exception ex) {
        logger.error("Exception error.", ex);
//        logClient.writeSystemLog(ex);
        return SystemErrorStatus.SYS_ERROR.toDataResult();
    }

    @ExceptionHandler(SupplierException.class)
    public DataResult handleSupplierException(SupplierException e) {
        logger.error("supplier: {}", e.getSupplier(), e);
        return DataResult.fail(SystemErrorStatus.SUPPLIER_EXCEPTION.getStatus(), e.getMessage());
    }


    @ExceptionHandler(AppException.class)
    public DataResult handleAppException(AppException e) {
        logger.error("", e);
        return DataResult.fail(SystemErrorStatus.SYS_ERROR.getStatus(), e.getMessage());
    }

    @ExceptionHandler(UnauthorizedException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public DataResult handleUnauthorizedException(UnauthorizedException ex) {
        int status = -401;
//        logClient.writeSystemLog(ex, LogLevel.WARN, status);
        return DataResult.fail(status, StringUtils.isEmpty(ex.getMessage())
                ? "未授权"
                : ex.getMessage());
    }

    @ExceptionHandler(ForbidAnonymousException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public DataResult handleForbidAnonymousException(ForbidAnonymousException ex) {
        int status = -403;
//        logClient.writeSystemLog(ex, LogLevel.WARN, status);
        return DataResult.fail(status, StringUtils.isEmpty(ex.getMessage())
                ? "禁止匿名访问"
                : ex.getMessage());
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public DataResult handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException ex) {
        logger.warn("", ex);
        ErrorStatus errorStatus = SystemErrorStatus.REQUEST_PARAMS_ERROR;
//        logClient.writeSystemLog(ex, LogLevel.WARN, errorStatus.getStatus());
        return DataResult.fail(errorStatus.getStatus(), ex.getMessage());
    }


    @ExceptionHandler(HttpMessageNotReadableException.class)
    public DataResult handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
        logger.warn("", ex);
        ErrorStatus errorStatus = SystemErrorStatus.REQUEST_PARAMS_ERROR;
//        logClient.writeSystemLog(ex, LogLevel.WARN, errorStatus.getStatus());
        return DataResult.fail(errorStatus.getStatus(), ex.getMessage());
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public DataResult handleMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        logger.warn("", ex);
        ErrorStatus errorStatus = SystemErrorStatus.REQUEST_PARAMS_ERROR;
//        logClient.writeSystemLog(ex, LogLevel.WARN, errorStatus.getStatus());
        return DataResult.fail(errorStatus.getStatus(), ex.getMessage());
    }

    @ExceptionHandler(MyBatisSystemException.class)
    public DataResult adviceMybatis(MyBatisSystemException ex) {
        logger.error("Mybatis System Exception error.", ex);
        ErrorStatus errorStatus = SystemErrorStatus.DATABASE_ERROR;
//        logClient.writeSystemLog(ex, LogLevel.ERROR, errorStatus.getStatus());
        return errorStatus.toDataResult();
    }

    @ExceptionHandler(ServletRequestBindingException.class)
    public DataResult handleRequestBindingException(ServletRequestBindingException ex) {
        logger.error("Request binding error", ex);
        ErrorStatus errorStatus = SystemErrorStatus.REQUEST_PARAMS_ERROR;
//        logClient.writeSystemLog(ex, LogLevel.WARN, errorStatus.getStatus());
        return errorStatus.toDataResult();
    }


    @ExceptionHandler(TypeMismatchException.class)
    public DataResult handleTypeMismatchException(TypeMismatchException ex) {
        logger.error("Type mismatch exception", ex);
        ErrorStatus errorStatus = SystemErrorStatus.REQUEST_PARAMS_ERROR;
//        logClient.writeSystemLog(ex, LogLevel.WARN, errorStatus.getStatus());
        return errorStatus.toDataResult();
    }

    @ExceptionHandler(SysException.class)
    public DataResult handleSysException(SysException ex) {
        logger.error("System exception", ex);
//        logClient.writeSystemLog(ex);
        return StringUtils.isEmpty(ex.getMessage())
                ? SystemErrorStatus.SYS_ERROR.toDataResult()
                : DataResult.fail(SystemErrorStatus.SYS_ERROR.getStatus(), ex.getMessage());
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public DataResult handleDuplicateKeyException(DuplicateKeyException ex) {
        log.warn("", ex);
        return SystemErrorStatus.DUPLICATE_KEY.toDataResult();
    }

    @ExceptionHandler(BusinessException.class)
    public DataResult handleBusinessException(BusinessException ex) {
        if (ex.getCause() == null) {
//            logClient.writeSystemLog(ex, LogLevel.INFO, ex.getCode());
            logger.warn("业务操作失败: [{}]{}", ex.getCode(), ex.getMessage());
        } else {
//            logClient.writeSystemLog(ex, LogLevel.WARN, ex.getCode());
            logger.error("业务操作异常: ", ex);
        }
        return ex.toDataResult();
    }

}
